package com.nulldev.util.web.YouTubeExtractor.signatures.extractors;

import com.nulldev.util.concurrency.threadIt.v4.emapi.ExecutorManager;
import com.nulldev.util.concurrency.threadIt.v4.emapi.uft.Executable;
import com.nulldev.util.data.Variables;
import com.nulldev.util.logging.Logger;
import com.nulldev.util.logging.LoggerFactory;
import com.nulldev.util.scripting.ScriptAPI.baseJS.BaseJS;
import com.nulldev.util.scripting.ScriptAPI.baseJS.legacy.lexer.BaseJSEnvironment;
import com.nulldev.util.scripting.ScriptAPI.baseJS.legacy.lexer.BaseJSFunction;
import com.nulldev.util.web.YouTubeExtractor.signatures.ExtractedSignatureData;
import com.nulldev.util.web.YouTubeExtractor.signatures.extractor.Extractor;

public class BaseJSExtractor extends Extractor {
	private static final Logger log = LoggerFactory.getLogger();

	/*
	 * BaseJS: Still less painful to maintain than nTube >1.4.4! This is the
	 * successor to the software extractor.
	 * 
	 * Decipherer level: v8: SUPPORTED, v9: UNSUPPORTED (work in progress)
	 */

	private static BaseJS baseJS;

	static {
		if (baseJS == null) {
			baseJS = new BaseJS();
			baseJS.useNewInterpreter = true;
		}
	}

	private BaseJSEnvironment environment;
	private Throwable t;

	public BaseJSExtractor(final ExtractedSignatureData data) {
		super(data);
		try {
			this.environment = baseJS.compile(data.getCode());
			log.info("BaseJS, version " + BaseJS.BASEJS_VERSION + ".");
		} catch (Throwable t) {
			this.t = t;
		}
	}

	@Override
	public Executable<String> decryptTask(final String signature) {
		return ExecutorManager.make(() -> {
			return decrypt(signature);
		});
	}

	@Override
	public String getCode() {
		return this.getExtractedData().getCode();
	}

	@Override
	public String getFunctionCode() {
		return this.getExtractedData().getFunctionCode();
	}

	@Override
	public String getFunctionName() {
		return this.getExtractedData().getFunctionName();
	}

	@Override
	public Extractor updateESD(ExtractedSignatureData newData) {
		if (newData == null)
			throw new NullPointerException("newData == null!");
		this.environment = baseJS.compile(newData.getCode());
		super.setExtractedData(newData);
		return this;
	}

	@Override
	public String decrypt(final String signature) {
		Variables.requireNonNull(this.environment, "environment");
		try {
			if (this.t != null) {
				throw new RuntimeException(this.t);
			} else if (signature == null || signature.isEmpty()) {
				return null;
			} else {
				final String sig = (String) ((BaseJSFunction) this.environment.get(getFunctionName())).run(baseJS.useNewInterpreter, this.environment,
						signature);
				return sig;
			}
		} catch (Throwable t) {
			log.error("Failed to decrypt signature!", t);
			return null;
		}
	}
}
